package com.xxx.chat.protocol;

import com.xxx.chat.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * 消息编码和解码器
 * 继承ByteToMessageCodec，无法解决半包和粘包问题
 */
@Slf4j
@ChannelHandler.Sharable  // @Sharable注解表示无状态
public class MessageCodec extends ByteToMessageCodec<Message> {

    // 编码器
    @Override
    public void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
        // 1. 4字节的魔数：1234
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2. 1字节的版本号
        out.writeByte(1);
        // 3. 1字节的序列化方式：jdk 0 , json 1
        out.writeByte(0);
        // 4. 1字节的指令类型（消息类型）
        out.writeByte(msg.getMessageType());
        // 5. 4个字节请求序号
        out.writeInt(msg.getSequenceId());
        // 无意义，1个字节对齐填充：1111 1111
        out.writeByte(0xff);
        // 6. 获取内容的字节数组：将Message对象转换成byte[]数组
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        byte[] bytes = bos.toByteArray();
        // 7. 4个字节消息长度
        out.writeInt(bytes.length);
        // 8. 写入消息正文内容
        out.writeBytes(bytes);
    }

    // 解码器
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 1. 4字节的魔数
        int magicNum = in.readInt();    // ByteBuf的read()方法会移动读指针，而get()方法不会移动指针
        // 2. 1字节的版本号
        byte version = in.readByte();
        // 3. 1字节的序列化方式：jdk 0 , json 1
        byte serializerType = in.readByte();
        // 4. 1字节的指令类型（消息类型）
        byte messageType = in.readByte();
        // 5. 4个字节请求序号
        int sequenceId = in.readInt();
        // 无意义，1个字节对齐填充
        in.readByte();
        // 6. 4个字节消息正文长度
        int length = in.readInt();
        // 7. 消息正文(byte数组)
        // 读取范围为[0, len]的内容到byte数组里，作为消息正文的内容
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length);
        // 将byte[]还原为Message对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Message message = (Message) ois.readObject();
        log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length);
        log.debug("{}", message);
        // 将msg对象添加到out列表中
        out.add(message);
    }
}